package util.compass;

import org.compass.core.Compass;
import org.compass.core.CompassCallback;
import org.compass.core.CompassDetachedHits;
import org.compass.core.CompassHits;
import org.compass.core.CompassQuery;
import org.compass.core.CompassSession;
import org.compass.core.CompassTemplate;
import org.compass.core.CompassTransaction;
import org.compass.core.support.search.CompassSearchCommand;
import org.compass.core.support.search.CompassSearchResults;
import org.compass.core.util.Assert;
import org.springframework.beans.factory.InitializingBean;

import util.compass.AdvancedSearchCommand.CompassSort;

public class CompassSearchService implements InitializingBean {

	// 每页显示的条目数量
	private Integer pageSize = 15;

	private Compass compass;

	private CompassTemplate compassTemplate;

	@SuppressWarnings("unchecked")
	public CompassSearchResults search(final CompassSearchCommand command) {
		return (CompassSearchResults) getCompassTemplate()
				.execute(
						CompassTransaction.TransactionIsolation.READ_ONLY_READ_COMMITTED,
						new CompassCallback() {
							public Object doInCompass(CompassSession session) {
								return performSearch(command, session);
							}
						});
	}

	protected CompassSearchResults performSearch(
			CompassSearchCommand searchCommand, CompassSession session) {
		long time = System.currentTimeMillis();
		CompassQuery query = buildQuery(searchCommand, session);
		CompassHits hits = query.hits();
		CompassDetachedHits detachedHits;
		CompassSearchResults.Page[] pages = null;
		if (pageSize == null) {
			doProcessBeforeDetach(searchCommand, session, hits, -1, -1);
			detachedHits = hits.detach();
		} else {
			int iPageSize = pageSize;
			int page = 0;
			int hitsLength = hits.getLength();
			if (searchCommand.getPage() != null) {
				page = searchCommand.getPage();
			}
			int from = page * iPageSize;

			if (from > hits.getLength()) {

				// 如果起始的条目大于搜索到的条目
				from = hits.getLength() - iPageSize;
				doProcessBeforeDetach(searchCommand, session, hits, from,
						hitsLength);
				detachedHits = hits.detach(from, hitsLength);
			} else if ((from + iPageSize) > hitsLength) {

				// 结束的条目大于搜索到的结果
				doProcessBeforeDetach(searchCommand, session, hits, from,
						hitsLength);
				detachedHits = hits.detach(from, hitsLength);
			} else {

				// 中间的页码，直接取出相应的条目
				doProcessBeforeDetach(searchCommand, session, hits, from,
						iPageSize);
				detachedHits = hits.detach(from, iPageSize);
			}
			doProcessAfterDetach(searchCommand, session, detachedHits);
			int numberOfPages = (int) Math.ceil((float) hitsLength / iPageSize);
			pages = new CompassSearchResults.Page[numberOfPages];
			for (int i = 0; i < pages.length; i++) {
				pages[i] = new CompassSearchResults.Page();
				pages[i].setFrom(i * iPageSize + 1);
				pages[i].setSize(iPageSize);
				pages[i].setTo((i + 1) * iPageSize);
				if (from >= (pages[i].getFrom() - 1) && from < pages[i].getTo()) {
					pages[i].setSelected(true);
				} else {
					pages[i].setSelected(false);
				}
			}
			if (numberOfPages > 0) {
				CompassSearchResults.Page lastPage = pages[numberOfPages - 1];
				if (lastPage.getTo() > hitsLength) {
					lastPage.setSize(hitsLength - lastPage.getFrom());
					lastPage.setTo(hitsLength);
				}
			}
		}
		time = System.currentTimeMillis() - time;
		CompassSearchResults searchResults = new CompassSearchResults(
				detachedHits.getHits(), time, pageSize);
		searchResults.setPages(pages);
		return searchResults;
	}

	protected CompassQuery buildQuery(CompassSearchCommand searchCommand,
			CompassSession session) {
		CompassQuery query = session.queryBuilder().queryString(
				searchCommand.getQuery().trim()).toQuery();

		if (AdvancedSearchCommand.class.isAssignableFrom(searchCommand
				.getClass())) {
			AdvancedSearchCommand advancedSearchCommand = (AdvancedSearchCommand) searchCommand;

			for (CompassSort sort : advancedSearchCommand.getSortMap()) {
				query.addSort(sort.getName(), sort.getType(), sort
						.getDirection());
			}
		}
		return query;
	}

	protected void doProcessBeforeDetach(CompassSearchCommand searchCommand,
			CompassSession session, CompassHits hits, int from, int size) {
		if (AdvancedSearchCommand.class.isAssignableFrom(searchCommand
				.getClass())) {
			if (from < 0) {
				from = 0;
				size = hits.getLength();
			}
			String[] highlightFields = ((AdvancedSearchCommand) searchCommand)
					.getHighlightFields();

			if (highlightFields == null) {
				return;
			}

			// highlight fields
			for (int i = from; i < size; i++) {
				for (String highlightField : highlightFields) {
					hits.highlighter(i).fragment(highlightField);
				}
			}
		}
	}

	protected void doProcessAfterDetach(CompassSearchCommand searchCommand,
			CompassSession session, CompassDetachedHits hits) {

	}

	public void afterPropertiesSet() throws Exception {
		Assert.notNull(compass, "Must set compass property");
		this.compassTemplate = new CompassTemplate(compass);
	}

	public Integer getPageSize() {
		return pageSize;
	}

	public void setPageSize(Integer pageSize) {
		this.pageSize = pageSize;
	}

	public void setCompass(Compass compass) {
		this.compass = compass;
	}

	protected CompassTemplate getCompassTemplate() {
		return this.compassTemplate;
	}
}
